package api

import (
	"encoding/json"
	"io"
	"net/http"
	"net/url"
	"unsafe"

	"gitee.com/knowgo/ego/core"
	"gitee.com/knowgo/ego/core/data"
	"gitee.com/knowgo/ego/core/errs"
	"gitee.com/knowgo/ego/egoc"
	"gitee.com/knowgo/ego/log"
)

const EgoBody = "ego.body"

type ApiHandler struct {
	core           *core.Core
	logRequest     bool
	log            log.Logger
	uploadMaxBytes int64 // 上传文件最大尺寸
	interceptors   []func(ctx *egoc.Context) bool
	corsConfigure  func(ctx *egoc.Context)
}

func NewApiHandler(core *core.Core, log log.Logger) *ApiHandler {
	return &ApiHandler{
		core:           core,
		log:            log,
		uploadMaxBytes: 32 << 20, // 默认限制 32M
	}
}

// AddInterceptor 添加拦截器
func (a *ApiHandler) AddInterceptor(interceptor ...func(ctx *egoc.Context) bool) *ApiHandler {
	a.interceptors = append(a.interceptors, interceptor...)
	return a
}

func (a *ApiHandler) SetLogRequest(log bool) *ApiHandler {
	a.logRequest = log
	return a
}

func (a *ApiHandler) ConfigureCors(fn func(ctx *egoc.Context)) *ApiHandler {
	a.corsConfigure = fn
	return a
}

func (a *ApiHandler) SetUploadMaxBytes(max int64) *ApiHandler {
	a.uploadMaxBytes = max
	return a
}

func bytes2str(buf []byte) string {
	return *(*string)(unsafe.Pointer(&buf))
}

func str2bytes(s string) []byte {
	x := (*[2]uintptr)(unsafe.Pointer(&s))
	h := [3]uintptr{x[0], x[1], x[1]}
	return *(*[]byte)(unsafe.Pointer(&h))
}

func (a *ApiHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	var (
		res            data.Response
		requests       data.Request
		err            error
		ignoreResponse bool
		context        = &egoc.Context{Request: request, Writer: writer}
	)
	if a.corsConfigure != nil {
		// 跨域配置
		a.corsConfigure(context)
		if request.Method == http.MethodOptions {
			// Options 请求
			return
		}
	}

	// 拦截器
	for _, inter := range a.interceptors {
		if !inter(context) {
			return
		}
	}

	defer func() {
		recover()
		if ignoreResponse {
			// 不做返回操作
			return
		}
		response, err := json.Marshal(res)
		if err != nil {
			a.log.Infof("response serialize error: %s", err.Error())
			res.Status = errs.StatusResponseSerialize
			res.StatusText = err.Error()
			res.Data.Response = nil
			res.Data.Extra = nil
			response, _ := json.Marshal(res)
			writer.Write(response)
		} else {
			if a.logRequest {
				a.log.Infof("response:\n%s", bytes2str(response))
			}
			writer.Write(response)
		}
	}()

	// 解析请求信息
	buf, err := a.parseRequest(request)
	if err != nil {
		res.Status = errs.StatusIllegalArgument
		res.Error = err
		res.StatusText = err.Error()
		res.Data.Response = nil
		panic(err)
	}
	requestData := bytes2str(buf)
	if a.logRequest {
		a.log.Info("request:\n", requestData)
	}
	// 解析
	if err := json.Unmarshal(str2bytes(requestData), &requests); err != nil {
		a.log.Error("parse data to requests error: %s\n%s", err.Error(), requestData)
		res.StatusText = err.Error()
		res.Error = err
		res.Status = errs.StatusIllegalArgument
		panic(err)
	}
	//

	serviceResponse, ignoreResponse, svcErr, err := a.core.Service(requests, context)
	if err != nil {
		a.log.Errorf("parse data to requests error: %s\n%s", err.Error(), requestData)
		res.StatusText = err.Error()
		res.Error = err
		if statusErr, ok := err.(*errs.ErrorWithStatus); ok {
			res.Status = statusErr.Status
		} else {
			var txtErr = errs.ParseTextError(err, svcErr)
			if txtErr != nil {
				res.StatusText = txtErr.Error()
			}
			res.Status = errs.StatusCustom
		}
		panic(err)
	}

	res.StatusText = "OK"
	res.Status = errs.StatusOK
	res.Data.Response = serviceResponse
	res.Data.Extra = context.GetExtra()
}

func (a *ApiHandler) parseRequest(request *http.Request) ([]byte, error) {
	var requestData []byte
	// var err error
	var bodyField = request.URL.Query().Get(EgoBody)
	if bodyField != "" {
		// 从 body 中读取 field 指定的字段值做为请求参数
		if err := request.ParseMultipartForm(a.uploadMaxBytes); err != nil {
			a.log.Error("parse mutipart form error: %s", err.Error())
			return nil, err
			// res.StatusText = err.Error()
			// res.Error = err
			// res.Status = errs.StatusIllegalArgument
			// panic(err)
		}
		requestData = str2bytes(request.FormValue(bodyField))
	} else if rawQuery := request.URL.RawQuery; rawQuery != "" {
		// 查看 url 中的查询参数是否为空
		str, err := url.QueryUnescape(rawQuery)
		if err != nil {
			a.log.Error("unescape query string error:\n%s", request.URL.RawQuery)
			// res.StatusText = err.Error()
			// res.Error = err
			// res.Status = errs.StatusIllegalArgument
			return nil, err
		}
		requestData = str2bytes(str)
	} else {
		// 将 body 作为请求参数
		buf, err := io.ReadAll(request.Body)
		if err != nil {
			a.log.Error("read body error: %s", err.Error())
			return nil, err
			// res.StatusText = err.Error()
			// res.Error = err
			// res.Status = errs.StatusIllegalArgument
			// panic(err)
		}
		requestData = buf //bytes2str(buf)
	}
	return requestData, nil
}
